/**
 * Computer Graphics Lecture WS 2009/2010 Ulm University
 * Creator: Manuel Finckh
 * Email:   manuel.finckh@uni-ulm.de
 */

#include "scene.h"
#include "utils/vec.h"
#include "utils/fileio.h"
#include <fstream>
#include <string>
#include <cstdio>
#include <iostream>
#include <vector>

using namespace std;

Scene::Scene(const char * file) {
  string tmp(file);
  if ( tmp.rfind("ra2") == tmp.length() - 3 ) LoadRa2(file);
 // else if ( tmp.rfind("srs") == tmp.length() - 3) LoadSrs(file);
  //else if ( tmp.rfind("mat") == tmp.length() - 3) LoadSrs(file);
  else {
    cerr << "can't load this file type" << endl;
  }
}

Scene::Scene(const char * file, int rx, int ry) {
	ResX = rx;
	ResY = ry;
  string tmp(file);
  if ( tmp.rfind("ra2") == tmp.length() - 3 ) LoadRa2(file);
  else if ( tmp.rfind("srs") == tmp.length() - 3) LoadSrs(file);
  else if ( tmp.rfind("mat") == tmp.length() - 3) LoadSrs(file);
  else {
    cerr << "can't load this file type" << endl;
  }
}

Scene::~Scene() {
  delete [] triangles;
  delete [] normals;
  delete [] uv;
  delete [] material;
  delete [] mat_index;
  delete [] environment;
  delete [] cam;
}

void Scene::loadEnvironment(char *filename){
	
	//load_image_ppm("env.ppm", ee, env_x, env_y);
	environment = (Vec3*) LoadHDRI(filename, env_x, env_y);
	//environment = new Vec3[env_x*env_y];
	std::cout<<"env_x: "<<env_x<<std::endl;
	std::cout<<"env_y: "<<env_y<<std::endl;
	/*for(int i = 0; i<env_x; i++){
		for(int j = 0; j<env_y; j++){
			environment[i*env_x + j].x = ee[3*(i*env_x + j)];
			environment[i*env_x + j].y = ee[3*(i*env_x + j)+1];
			environment[i*env_x + j].z = ee[3*(i*env_x + j)+2];
		}
	}*/
}

bool Scene::LoadRa2(const char * file) {
  // Triangles
  float *tris;
  num_tris = load_float_data(file, tris) / (3*3);
  triangles = (Triangle*)tris;
  if (num_tris == 0) return false;

  // Shading Normals
  string tmp(file);
  int pos = tmp.find_last_of(".");
  tmp.assign(tmp, 0, pos+1);
  tmp.append("n");
  //cout<<tmp<<endl;
  float *norms;
  if ( load_float_data(tmp.c_str(), norms) == 0 ) {
    normals = new Vec3[3 * num_tris];
    //cout<<"shading normals ftw"<<endl;
    for ( int i = 0; i < num_tris; i++ ) {
      Vec3 tri_normal = triangles[i].getNormal();
      normals[3 * i    ] = tri_normal;
      normals[3 * i + 1] = tri_normal;
      normals[3 * i + 2] = tri_normal;
    }
  } else normals = (Vec3*)norms;
  
  // Material
  pos = tmp.find_last_of(".");
  tmp.assign(tmp, 0, pos+1);
  tmp.append("mat");
  if(!LoadSrs(tmp.c_str())){
	  Vec3 color_d(0.8f, 0.7f, 0.2f);
	  Vec3 null(0.0f);
	  Material mat(color_d, null, null, null, 0.0f, 1.0f, 1.0f, NULL, 0, num_tris - 1);
	  material = new Material[1];
	  material[0] = mat;
	  num_material = 1;
	  
	  mat_index = new int[num_tris];
  	  for ( int i = 0; i < num_tris; i++ ) mat_index[i] = 0;
  }

  

  // Camera
  AABB bbox = Triangle::getAABB(triangles, num_tris);
  cam = new Cam(bbox, ResX, ResY);
  

  return true;
}

bool Scene::LoadSrs(const char * file) {
  string directory(file);
  cout<<"file: "<<file<<endl;
  int pos = directory.find_last_of("/");
  directory.assign(directory, 0, pos+1);

  fstream f;
  f.open(file, ios::in);
  if ( !f ) return false;

  string ra2(directory), ran(directory), ruv(directory);
  vector<Material> material0;
  vector<string> mat_names;
  vector<Light> lights0;
  int num_objects;
  int ResX = 300, ResY = 300;

  string line;
  while ( getline(f, line) ) {
    if ( line.find("#") == 0 || line.length() == 0 ) continue;
    if ( line.find("version 0.3.3") == 0 ) { cout << "Versions match: 0.3.3" << endl; continue; }
    if ( line.find("triangles") == 0 ) {
      char c_t0[20], c_t1[20], c_t2[20];
      sscanf(line.c_str(), "%*s \"%[^\"] %*2c \"%[^\"] %*2c \"%[^\"]", c_t0, c_t1, c_t2);
      ra2.append(c_t0); ran.append(c_t1); ruv.append(c_t2);
      continue;
    }
    if ( line.find("environment") == 0 ) {
      char env_name[30];
      sscanf(line.c_str(), "%*s %d %d %*f %*f %*f \"%[^\"]", &ResX, &ResY, env_name);
      string map = directory + "textures/" + string(env_name) + ".hdr";
      float *env_data;
      load_image_hdr(map.c_str(), env_data, env_x, env_y);
      environment = (Vec3*)env_data;
      continue;
    }
    if ( line.find("camera") == 0 ) {
      float focalL, focalD, lensR, filmH, pixelA;
      Vec3 e, w, v, u;
      sscanf(line.c_str(), "%*s %*s focalL=%f focalD =%f lensR=%f filmHeight=%f pixelAspect=%f", &focalL, &focalD, &lensR, &filmH, &pixelA);
      pos = line.find("pixelAspect="); line.assign(line, pos, line.length()); // split -> less sscanf confuse
      sscanf(line.c_str(), "%*s %f %f %f %f %f %f %f %f %f %f %f %f", &e[0], &e[1], &e[2], &u[0], &u[1], &u[2], &v[0], &v[1], &v[2], &w[0], &w[1], &w[2]);
      w = -w;
      cam = new Cam(e, w, v, 0.08f, ResX, ResY);
      continue;
    }
    if ( line.find("material blender") == 0 ) {
      float eta, hard, attd;
      Vec3 col0d, col90d, colT, emmit;
      char mat_name[30], col0d_s[30], col90d_s[30], colT_s[30], emmit_s[30], col0tex[30];

      sscanf(line.c_str(), "%*s %*s \"%[^\"] %*2c col0d=%s", mat_name, col0d_s);
      pos = line.find("col90d="); line.assign(line, pos+7, line.length()-7); // split -> less sscanf confuse
      sscanf(line.c_str(), "%s eta=%f hard=%f colT=%s attd=%f emmit=%s col0tex=\"%[^\"]", col90d_s, &eta, &hard, colT_s, &attd, emmit_s, col0tex);

      sscanf(col0d_s , "(%f,%f,%f)", &col0d[0] , &col0d[1] , &col0d[2] );
      sscanf(col90d_s, "(%f,%f,%f)", &col90d[0], &col90d[1], &col90d[2]);
      sscanf(colT_s  , "(%f,%f,%f)", &colT[0]  , &colT[1]  , &colT[2]  );
      sscanf(emmit_s , "(%f,%f,%f)", &emmit[0] , &emmit[1] , &emmit[2] );
      

      Texture *texture = NULL;
      string tex = col0tex;
      if ( tex.find("none") != 0 ) {
        tex = directory + "textures/" + tex + ".ppm";
        float *image;
        int XX, YY;
        load_image_ppm(tex.c_str(), image, XX, YY);
        texture = new Texture(XX, YY, (Vec3*)image);

        cout << tex << endl;
      }

      Material tmp_mat(col0d, col90d, colT, emmit, hard, eta, attd, texture, -1, -1);
      mat_names.push_back(mat_name);
      material0.push_back(tmp_mat);

      // TODO: Textures, Bump Map, Normal Map
      continue;
    }
    if ( line.find("objects") == 0 ) {
      sscanf(line.c_str(), "%*s %d", &num_objects);
      break;
    }
  }

  if ( cam == NULL ) {
  }
  float *tris, *norms, *uvs;
  num_tris = load_float_data(ra2.c_str(), tris) / (3*3);
  triangles = (Triangle*)tris;
  cout<<ran<<endl;
  load_float_data(ran.c_str(), norms);
  normals = (Vec3*)norms;
  load_float_data(ruv.c_str(), uvs);
  uv = (Vec2*)uvs;

  mat_index = new int[num_tris];
  material = new Material[num_objects];
  num_material = num_objects;

  int counter = 0;
  float summed_area = 0.0;
  for ( int i = 0; i < num_objects; i++ ) {
    getline(f, line);
    char c0[90]; int start, end;
    sscanf(line.c_str(), "%*s \"%[^\"] %*c %d %d", c0, &start, &end);

    string mat_s(c0);
    for ( unsigned int j = 0; j < mat_names.size(); j++ ) {
      if ( mat_names[j] == mat_s ) {
      	cout<<mat_s<<endl;
        material[counter] = material0[j];
        material[counter].start = start;
        material[counter].end = end;
        material[counter].name = mat_s;
        
        // light?
        if(material[counter].color_e.length_sqr() > 0.000001){
        	for ( int k = start; k <= end; k++ ) {
        		float ar = triangles[k].area();
          		Light tmp_light(k,ar);
          		summed_area += ar;
          		lights0.push_back(tmp_light);
          	}
        }
        	
        for ( int k = start; k <= end; k++ ) {
          mat_index[k] = counter;
          
        }
        counter++;
        break;
      }
    }
  }
  f.close();
  
  num_lights = lights0.size();
  lights = new Light[num_lights];
  light_pdf = new float[num_lights];
  lights[0] = lights0[0];
  for(int i = 1; i< num_lights; i++){
  	lights[i] = lights0[i];
  	light_pdf[i] = light_pdf[i-1] + lights[i].area / summed_area;
  }
  cout<<"lightcount: "<<num_lights<<"  sum: "<<summed_area<<endl;
  
  
  return true;
}

Vec3 Scene::sampleRandomLight(int &id, float r0, float r1, float r2){
	
	int index = 0;
	while(index < (num_lights-1) && light_pdf[index] < r0){
		//cout<<"val: "<<light_pdf[index]<<"  "<<index<<endl;
		index++;
		//cout<<"val: "<<light_pdf[index]<<"  "<<index<<endl;
	}
	//cout<<"val: "<<light_pdf[index]<<"  "<<index<<endl;	
	Vec3 p;
	float alpha, beta;
	triangles[lights[index].index].sample(r1, r2, p, alpha, beta);
	
	id = lights[index].index;
	return p;

}




